﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace dPet.Application.Services.Implementation
{
	using AutoMapper;
	using dPet.Application.DataObjects;
	using dPet.Domain.Models;
	using dPet.Domain.Repositories;
	using dPet.Domain.Services;
	using dPet.Domain.Specifications;
	using dPet.Infrastructure.Logging;
	using Hangerd;
	using Hangerd.Repository;

	public class ServiceManagementService : ServiceBase, IServiceManagementService
	{
		#region Repository

		private readonly IHospitalRepository _hospitalRepository;
		private readonly IServiceCategoryRepository _serviceCategoryRepository;
		private readonly IServiceSKUPropertyRepository _serviceSKUPropertyRepository;
		private readonly IServiceItemRepository _serviceItemRepository;
		private readonly IServiceItemSKURepository _serviceItemSKURepository;

		#endregion

		#region Services

		private readonly IServingDomainService _servingDomainService;

		#endregion

		#region Constructors

		public ServiceManagementService(
			IRepositoryContext unitOfWork,
			IHospitalRepository hospitalRepository,
			IServiceCategoryRepository serviceCategoryRepository,
			IServiceSKUPropertyRepository serviceSKUPropertyRepository,
			IServiceItemRepository serviceItemRepository,
			IServiceItemSKURepository serviceItemSKURepository,
			IServingDomainService serviceSKUDomainService)
			: base(unitOfWork)
		{
			_hospitalRepository = hospitalRepository;
			_serviceCategoryRepository = serviceCategoryRepository;
			_serviceSKUPropertyRepository = serviceSKUPropertyRepository;
			_serviceItemRepository = serviceItemRepository;
			_serviceItemSKURepository = serviceItemSKURepository;
			_servingDomainService = serviceSKUDomainService;
		}

		#endregion

		#region Category

		public MessagedResult<bool> AddServiceCategory(ServiceCategoryDto categoryDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务类别管理", "添加分类", (ref ServiceCategory category) =>
			{
				var hospital = _hospitalRepository.Get(categoryDto.HospitalId, false);

				if (hospital == null)
				{
					throw new HangerdException("医院信息不存在");
				}

				category = new ServiceCategory(hospital, categoryDto.Name);

				_serviceCategoryRepository.Add(category, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> UpdateServiceCategory(string categoryId, ServiceCategoryDto categoryDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务类别管理", "更新分类", (ref ServiceCategory category) =>
			{
				category = _serviceCategoryRepository.Get(categoryId, true);

				if (category == null)
				{
					throw new HangerdException("分类信息不存在");
				}

				category.Name = categoryDto.Name;

				_serviceCategoryRepository.Update(category, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public IEnumerable<ServiceCategoryDto> GetServiceCategories(string hospitalId)
		{
			var spec = ServiceCategorySpecifications.HospitalIdEquals(hospitalId);

			var categories = _serviceCategoryRepository.GetAll(spec, false)
				.OrderBy(c => c.CreateTime);

			return Mapper.Map<IEnumerable<ServiceCategory>, IEnumerable<ServiceCategoryDto>>(categories);
		}

		public ServiceCategoryDto GetServiceCategory(string id)
		{
			var category = _serviceCategoryRepository.Get(id, false);

			return Mapper.Map<ServiceCategory, ServiceCategoryDto>(category);
		}

		#endregion

		#region SKU Property

		public MessagedResult<bool> AddServiceSKUProperty(ServiceSKUPropertyDto skuPropertyDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务SKU属性管理", "添加SKU属性", (ref ServiceSKUProperty skuProperty) =>
			{
				var hospital = _hospitalRepository.Get(skuPropertyDto.HospitalId, false);

				if (hospital == null)
				{
					throw new HangerdException("医院信息不存在");
				}

				skuProperty = new ServiceSKUProperty(hospital, skuPropertyDto.FrontendName, skuPropertyDto.BackendName);

				var category = _serviceCategoryRepository.Get(skuPropertyDto.Category.Id, true);

				skuProperty.Categorize(category);

				_serviceSKUPropertyRepository.Add(skuProperty, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> UpdateServiceSKUProperty(string propertyId, ServiceSKUPropertyDto skuPropertyDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务SKU属性管理", "更新SKU属性", (ref  ServiceSKUProperty skuProperty) =>
			{
				skuProperty = _serviceSKUPropertyRepository.Get(propertyId, true, p => p.Category);

				if (skuProperty == null)
				{
					throw new HangerdException("服务SKU属性不存在");
				}

				skuProperty.FrontendName = skuPropertyDto.FrontendName;
				skuProperty.BackendName = skuPropertyDto.BackendName;

				var category = _serviceCategoryRepository.Get(skuPropertyDto.Category.Id, true);

				skuProperty.Categorize(category);

				_serviceSKUPropertyRepository.Update(skuProperty, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public IEnumerable<ServiceSKUPropertyDto> GetServiceSKUProperties(string hospitalId, bool loadCategory)
		{
			var eagerLoadingProperties = new List<Expression<Func<ServiceSKUProperty, dynamic>>>();

			if (loadCategory)
			{
				eagerLoadingProperties.Add(ssp => ssp.Category);
			}

			var spec = ServiceSKUPropertySpecifications.HospitalIdEquals(hospitalId);

			var properties = _serviceSKUPropertyRepository.GetAll(spec, false, eagerLoadingProperties.ToArray())
				.OrderBy(ssp => ssp.BackendName);

			return Mapper.Map<IEnumerable<ServiceSKUProperty>, IEnumerable<ServiceSKUPropertyDto>>(properties);
		}

		public IEnumerable<ServiceSKUPropertyDto> GetServiceSKUPropertiesByCategory(string categoryId)
		{
			var spec = ServiceSKUPropertySpecifications.CategoryIdEquals(categoryId);

			var properties = _serviceSKUPropertyRepository.GetAll(spec, false)
				.OrderBy(ssp => ssp.BackendName);

			return Mapper.Map<IEnumerable<ServiceSKUProperty>, IEnumerable<ServiceSKUPropertyDto>>(properties);
		}

		public ServiceSKUPropertyDto GetServiceSKUProperty(string id, bool loadCategory, bool loadValues)
		{
			var eagerLoadingProperties = new List<Expression<Func<ServiceSKUProperty, dynamic>>>();

			if (loadCategory)
			{
				eagerLoadingProperties.Add(ssp => ssp.Category);
			}

			if (loadValues)
			{
				eagerLoadingProperties.Add(ssp => ssp.Values);
			}

			var property = _serviceSKUPropertyRepository.Get(id, false, eagerLoadingProperties.ToArray());

			return Mapper.Map<ServiceSKUProperty, ServiceSKUPropertyDto>(property);
		}

		public MessagedResult<bool> SaveServiceSKUPropertyValue(string skuPropertyId, ServiceSKUPropertyValueDto skuPropertyValueDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务SKU属性管理", "保存SKU属性值", (ref ServiceSKUProperty skuProperty) =>
			{
				skuProperty = _serviceSKUPropertyRepository.Get(skuPropertyId, true, skup => skup.Values);

				if (skuProperty == null)
				{
					throw new HangerdException("SKU属性不存在");
				}

				if (string.IsNullOrWhiteSpace(skuPropertyValueDto.Id))
				{
					skuProperty.AddValue(skuPropertyValueDto.Value);
				}
				else
				{
					skuProperty.UpdateValue(skuPropertyValueDto.Id, skuPropertyValueDto.Value);
				}

				_serviceSKUPropertyRepository.Update(skuProperty, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		#endregion

		#region Service Item

		public MessagedResult<bool> AddServiceItem(ServiceItemDto serviceItemDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项管理", "添加服务项", (ref ServiceItem serviceItem) =>
			{
				var hospital = _hospitalRepository.Get(serviceItemDto.HospitalId, false);

				if (hospital == null)
				{
					throw new HangerdException("医院信息不存在");
				}

				serviceItem = new ServiceItem(hospital, serviceItemDto.Name);

				var category = _serviceCategoryRepository.Get(serviceItemDto.Category.Id, true);

				serviceItem.Categorize(category);

				_serviceItemRepository.Add(serviceItem, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> UpdateServiceItem(string itemId, ServiceItemDto serviceItemDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项管理", "更新服务项", (ref  ServiceItem serviceItem) =>
			{
				serviceItem = _serviceItemRepository.Get(itemId, true, p => p.Category);

				if (serviceItem == null)
				{
					throw new HangerdException("服务项不存在");
				}

				serviceItem.Name = serviceItemDto.Name;

				var category = _serviceCategoryRepository.Get(serviceItemDto.Category.Id, true);

				serviceItem.Categorize(category);

				_serviceItemRepository.Update(serviceItem, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public IEnumerable<ServiceItemDto> GetServiceItems(string hospitalId, bool loadCategory, bool loadSKUProperties)
		{
			var eagerLoadingProperties = new List<Expression<Func<ServiceItem, dynamic>>>();

			if (loadCategory)
			{
				eagerLoadingProperties.Add(si => si.Category);
			}

			if (loadSKUProperties)
			{
				eagerLoadingProperties.Add(si => si.SKUProperties);
			}

			var spec = ServiceItemSpecifications.HospitalIdEquals(hospitalId);

			var items = _serviceItemRepository.GetAll(spec, false, eagerLoadingProperties.ToArray())
				.OrderBy(si => si.CreateTime);

			return Mapper.Map<IEnumerable<ServiceItem>, IEnumerable<ServiceItemDto>>(items);
		}

		public IEnumerable<ServiceItemDto> GetServiceItemsByCategory(string categoryId)
		{
			var spec = ServiceItemSpecifications.CategoryIdEquals(categoryId);

			var serviceItems = _serviceItemRepository.GetAll(spec, false)
				.OrderBy(si => si.CreateTime);

			return Mapper.Map<IEnumerable<ServiceItem>, IEnumerable<ServiceItemDto>>(serviceItems);
		}

		public ServiceItemDto GetServiceItem(string id, bool loadCategory, bool loadSKUProperties, bool loadSKUList)
		{
			var eagerLoadingProperties = new List<Expression<Func<ServiceItem, dynamic>>>();

			if (loadCategory)
			{
				eagerLoadingProperties.Add(si => si.Category);
			}

			if (loadSKUProperties)
			{
				eagerLoadingProperties.Add(si => si.SKUProperties);
			}

			if (loadSKUList)
			{
				eagerLoadingProperties.Add(si => si.SKUList);
			}

			var item = _serviceItemRepository.Get(id, false, eagerLoadingProperties.ToArray());

			return Mapper.Map<ServiceItem, ServiceItemDto>(item);
		}

		#endregion

		#region Service Item Properties

		public MessagedResult<bool> AddSKUPropertyToServiceItem(string serviceItemId, string skuPropertyId, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项管理", "添加SKU属性", (ref ServiceItem serviceItem) =>
			{
				serviceItem = _serviceItemRepository.Get(serviceItemId, true, si => si.SKUProperties);

				if (serviceItem == null)
				{
					throw new HangerdException("服务项不存在");
				}

				var skuProperty = _serviceSKUPropertyRepository.Get(skuPropertyId, true);

				if (skuProperty == null)
				{
					throw new HangerdException("SKU属性不存在");
				}

				serviceItem.AddSKUProperty(skuProperty);

				_serviceItemRepository.Update(serviceItem, false);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> RemoveSKUPropertyFromServiceItem(string serviceItemId, string skuPropertyId, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项管理", "移除SKU属性", (ref ServiceItem serviceItem) =>
			{
				serviceItem = _serviceItemRepository.Get(serviceItemId, true, si => si.SKUProperties);

				if (serviceItem == null)
				{
					throw new HangerdException("服务项不存在");
				}

				var skuProperty = _serviceSKUPropertyRepository.Get(skuPropertyId, true);

				if (skuProperty == null)
				{
					throw new HangerdException("SKU属性不存在");
				}

				serviceItem.RemoveSKUProperty(skuProperty);

				_serviceItemRepository.Update(serviceItem, false);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		#endregion

		#region Service Item SKU

		public IEnumerable<ServiceItemSKUDto> GetServiceItemSKUListForMembership(string serviceItemId, MembershipLevelDto membershipLevelDto)
		{
			var spec = ServiceItemSKUSpecifications.ServiceItemIdEquals(serviceItemId);

			var skuList = _serviceItemSKURepository.GetAll(spec, false, sisku => sisku.ServiceItem);

			return Mapper.Map<IEnumerable<ServiceItemSKU>, IEnumerable<ServiceItemSKUDto>>(skuList);
		}

		public MessagedResult<bool> RegenerateSKUForServiceItem(string serviceItemId, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项SKU管理", "重新生成SKU", (ref ServiceItem serviceItem) =>
			{
				serviceItem = _serviceItemRepository.Get(serviceItemId, true, si => si.SKUProperties, si => si.SKUList);

				if (serviceItem == null)
				{
					throw new HangerdException("服务项不存在");
				}

				//移除当前所有SKU
				_serviceItemSKURepository.RemoveAllByServiceItem(serviceItem);

				//重新生成SKU
				_servingDomainService.GenerateServiceItemSKU(serviceItem);

				_serviceItemRepository.Update(serviceItem, false);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> RemoveSKUFromServiceItem(string serviceItemSKUId, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项SKU管理", "删除SKU", (ref ServiceItemSKU serviceItemSKU) =>
			{
				serviceItemSKU = _serviceItemSKURepository.Get(serviceItemSKUId, true);

				if (serviceItemSKU == null)
				{
					throw new HangerdException("服务项SKU不存在");
				}

				_serviceItemSKURepository.Delete(serviceItemSKU);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		public MessagedResult<bool> UpdateServiceItemSKU(string serviceItemSKUId, ServiceItemSKUDto skuDto, IOperator @operator)
		{
			return TryLoggedOperate(@operator, "服务项SKU管理", "更新SKU", (ref ServiceItemSKU serviceItemSKU) =>
			{
				serviceItemSKU = _serviceItemSKURepository.Get(serviceItemSKUId, true);

				if (serviceItemSKU == null)
				{
					throw new HangerdException("服务项SKU不存在");
				}

				serviceItemSKU.SetUnitPrice(skuDto.UnitPrice);

				_serviceItemSKURepository.Update(serviceItemSKU, true);
				
				UnitOfWork.Commit();

				return true;
			});
		}

		#endregion
	}
}
